from flask import Flask, render_template, jsonify, request
import os
import pandas as pd
from datetime import datetime
from werkzeug.utils import secure_filename
import requests
import json
import logging
import logging.handlers
import sys
from pathlib import Path


app = Flask(__name__)
app.config['MAX_CONTENT_LENGTH'] = 10 * 1024 * 1024  # 10MB 限制

# 上传文件夹配置
UPLOAD_FOLDER = 'uploads'
for folder in ['delivery', 'workload', 'defect']:
    folder_path = os.path.join(UPLOAD_FOLDER, folder)
    if not os.path.exists(folder_path):
        os.makedirs(folder_path)

app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER

# 创建日志配置函数
def setup_logging():
    """设置详细的日志配置"""
    # 创建logs目录
    log_dir = Path('logs')
    log_dir.mkdir(exist_ok=True)
    
    # 配置日志格式
    detailed_formatter = logging.Formatter(
        '%(asctime)s - %(name)s - %(levelname)s - %(funcName)s:%(lineno)d - %(message)s',
        datefmt='%Y-%m-%d %H:%M:%S'
    )
    
    simple_formatter = logging.Formatter(
        '%(asctime)s - %(levelname)s - %(message)s',
        datefmt='%H:%M:%S'
    )
    
    # 根日志器配置
    root_logger = logging.getLogger()
    root_logger.setLevel(logging.DEBUG)
    
    # 控制台处理器
    console_handler = logging.StreamHandler(sys.stdout)
    console_handler.setLevel(logging.INFO)
    console_handler.setFormatter(simple_formatter)
    root_logger.addHandler(console_handler)
    
    # 文件处理器 - 详细日志
    file_handler = logging.handlers.RotatingFileHandler(
        log_dir / 'app_detailed.log',
        maxBytes=10*1024*1024,  # 10MB
        backupCount=5
    )
    file_handler.setLevel(logging.DEBUG)
    file_handler.setFormatter(detailed_formatter)
    root_logger.addHandler(file_handler)
    
    # 文件处理器 - 错误日志
    error_handler = logging.handlers.RotatingFileHandler(
        log_dir / 'app_errors.log',
        maxBytes=10*1024*1024,
        backupCount=3
    )
    error_handler.setLevel(logging.ERROR)
    error_handler.setFormatter(detailed_formatter)
    root_logger.addHandler(error_handler)
    
    # 缺陷数据专用日志器
    defect_logger = logging.getLogger('defect_data')
    defect_handler = logging.handlers.RotatingFileHandler(
        log_dir / 'defect_data.log',
        maxBytes=5*1024*1024,
        backupCount=3
    )
    defect_handler.setLevel(logging.DEBUG)
    defect_handler.setFormatter(detailed_formatter)
    defect_logger.addHandler(defect_handler)
    defect_logger.propagate = False
    
    # 性能监控日志器
    perf_logger = logging.getLogger('performance')
    perf_handler = logging.handlers.RotatingFileHandler(
        log_dir / 'performance.log',
        maxBytes=5*1024*1024,
        backupCount=2
    )
    perf_handler.setLevel(logging.INFO)
    perf_handler.setFormatter(detailed_formatter)
    perf_logger.addHandler(perf_handler)
    perf_logger.propagate = False
    
    return logging.getLogger(__name__)

# 初始化日志
logger = setup_logging()
logger.info("应用启动 - 日志系统初始化完成")

# 存储解析后的交付数据
delivery_data = []

# 添加统一的日期解析函数
# 改进后的日期解析函数
def parse_date_to_date(date_value):
    """将各种格式的日期值转换为datetime.date对象，只保留年月日"""
    if not date_value:
        return None
    
    try:
        if isinstance(date_value, datetime):
            result = date_value.date()
            # print(f"📅 日期解析: {date_value} -> {result}")
            return result
        elif isinstance(date_value, str):
            # 移除时间部分（时分秒）
            date_str = date_value.split(' ')[0].split('T')[0]
            # print(f"📅 处理字符串日期: {date_value} -> {date_str}")
            
            # 尝试多种日期格式
            date_formats = ['%Y-%m-%d', '%Y/%m/%d', '%m/%d/%Y', '%d/%m/%Y', '%Y.%m.%d']
            for fmt in date_formats:
                try:
                    result = datetime.strptime(date_str.strip(), fmt).date()
                    # print(f"📅 成功解析: {date_str} 使用格式 {fmt} -> {result}")
                    return result
                except ValueError:
                    continue
            # 如果都不匹配，尝试pandas自动解析
            result = pd.to_datetime(date_str).date()
            # print(f"📅 pandas解析: {date_str} -> {result}")
            return result
        elif isinstance(date_value, pd.Timestamp):
            result = date_value.date()
            # print(f"📅 Timestamp解析: {date_value} -> {result}")
            return result
        elif isinstance(date_value, (int, float)):
            # 处理Excel序列号日期
            result = pd.to_datetime(date_value, unit='D', origin='1899-12-30').date()
            # print(f"📅 Excel序列号解析: {date_value} -> {result}")
            return result
        else:
            # 对于其他类型，尝试转换为字符串再解析
            date_str = str(date_value).split(' ')[0].split('T')[0]
            result = pd.to_datetime(date_str).date()
            # print(f"📅 其他类型解析: {date_value} -> {result}")
            return result
    except (ValueError, TypeError) as e:
        print(f"[WARNING] 日期解析失败: {date_value}, 错误: {e}")
        return None

# 存储解析后的交付数据和工作量数据
delivery_data = []
workload_data = []
defect_data = []  # 新增：缺陷数据存储

# 添加工作量日期解析函数
def parse_workload_date(date_value):
    """解析工作量数据中的日期字段，支持多种格式"""
    if not date_value:
        return None
    
    try:
        if isinstance(date_value, datetime):
            return date_value.date()
        elif isinstance(date_value, str):
            # 清理字符串
            date_str = date_value.strip()
            if not date_str:
                return None
                
            # 支持多种日期格式
            date_formats = [
                '%Y-%m-%d', '%Y/%m/%d', '%m/%d/%Y', '%d/%m/%Y',
                '%Y-%m-%d %H:%M:%S', '%Y/%m/%d %H:%M:%S',
                '%Y年%m月%d日', '%m月%d日', '%Y.%m.%d'
            ]
            
            for fmt in date_formats:
                try:
                    parsed_date = datetime.strptime(date_str, fmt).date()
                    logger.debug(f"✅ 日期解析成功: '{date_value}' -> {parsed_date} (格式: {fmt})")
                    return parsed_date
                except ValueError:
                    continue
                    
            # 如果都不匹配，尝试pandas自动解析
            try:
                parsed_date = pd.to_datetime(date_value).date()
                logger.debug(f"✅ 日期解析成功(pandas): '{date_value}' -> {parsed_date}")
                return parsed_date
            except:
                logger.warning(f"⚠️ 日期解析失败: '{date_value}'")
                return None
                
        elif isinstance(date_value, pd.Timestamp):
            return date_value.date()
        else:
            return pd.to_datetime(str(date_value)).date()
    except Exception as e:
        logger.warning(f"⚠️ 日期解析异常: '{date_value}' - {e}")
        return None


# 替换calculate_workload_stats函数（第120-180行）
def calculate_workload_stats(start_date, end_date):
    """根据时间筛选计算工作量统计数据"""
    try:
        start_dt = datetime.strptime(start_date, '%Y-%m-%d').date()
        end_dt = datetime.strptime(end_date, '%Y-%m-%d').date()
        
        logger.info(f"🔍 工作量数据筛选 - 时间范围: {start_date} 到 {end_date}")
        logger.info(f"📊 原始工作量数据量: {len(workload_data)}")
        
        # 添加数据检查
        if not workload_data:
            logger.warning("⚠️ 工作量数据为空，请检查数据加载")
            return []
        
        workload_stats = []
        filtered_count = 0
        skipped_count = 0
        
        for i, record in enumerate(workload_data):
            # 获取统计周期的开始和结束时间
            period_start = None
            period_end = None
            
            # 尝试解析统计周期开始时间
            if '统计周期开始' in record and record['统计周期开始']:
                period_start = parse_workload_date(record['统计周期开始'])
                if period_start is None:
                    logger.warning(f"⚠️ 记录 {i} 开始时间解析失败: {record['统计周期开始']}")
            
            # 尝试解析统计周期结束时间
            if '统计周期结束' in record and record['统计周期结束']:
                period_end = parse_workload_date(record['统计周期结束'])
                if period_end is None:
                    logger.warning(f"⚠️ 记录 {i} 结束时间解析失败: {record['统计周期结束']}")
            
            # 调试输出前几条记录的信息
            if i < 10:
                logger.info(f"🔍 记录 {i} - 人员: {record.get('人员名称', 'N/A')}, 开始时间: {period_start}, 结束时间: {period_end}")
            
            # 检查记录的统计周期是否与筛选时间范围有重叠
            should_include = False
            skip_reason = ""
            
            # 如果没有时间信息，则跳过该记录
            if not period_start and not period_end:
                skip_reason = "无时间信息"
                skipped_count += 1
                
            # 如果有开始时间但没有结束时间，只检查开始时间是否在范围内
            elif period_start and not period_end:
                if start_dt <= period_start <= end_dt:
                    should_include = True
                else:
                    skip_reason = f"开始时间 {period_start} 不在范围 [{start_dt}, {end_dt}] 内"
                    skipped_count += 1
                    
            # 如果有结束时间但没有开始时间，只检查结束时间是否在范围内
            elif not period_start and period_end:
                if start_dt <= period_end <= end_dt:
                    should_include = True
                else:
                    skip_reason = f"结束时间 {period_end} 不在范围 [{start_dt}, {end_dt}] 内"
                    skipped_count += 1
                    
            # 如果开始和结束时间都有，检查时间段是否与筛选范围有重叠
            elif period_start and period_end:
                # 特殊情况：如果开始时间和结束时间相同（来自同一个统计周期字段）
                if period_start == period_end:
                    # 当作单一时间点处理
                    if start_dt <= period_start <= end_dt:
                        should_include = True
                    else:
                        skip_reason = f"统计周期时间点 {period_start} 不在范围 [{start_dt}, {end_dt}] 内"
                        skipped_count += 1
                else:
                    # 两个时间段有重叠的条件：
                    # record的开始时间 <= 筛选的结束时间 且 record的结束时间 >= 筛选的开始时间
                    if period_start <= end_dt and period_end >= start_dt:
                        should_include = True
                    else:
                        skip_reason = f"时间段 [{period_start}, {period_end}] 与筛选范围 [{start_dt}, {end_dt}] 无重叠"
                        skipped_count += 1
            
            # 记录筛选结果
            if should_include:
                filtered_count += 1
                if i < 10:
                    logger.info(f"✅ 记录 {i} - 保留: 时间段符合条件")
            else:
                if i < 10:
                    logger.info(f"❌ 记录 {i} - 跳过: {skip_reason}")
                continue
            
            # 提取数据字段
            user_name = str(record.get('人员名称', '未知人员'))
            saturation = float(record.get('饱和度', 0))
            code_equivalent = float(record.get('代码当量', 0))
            total_hours = float(record.get('总工时', 0))
            schedule_hours = float(record.get('排期工时', 0))
            ai_active_days = int(record.get('AI活跃天数', 0))
            
            workload_stats.append({
                'user_name': user_name,
                'saturation': round(saturation, 1),
                'code_equivalent': round(code_equivalent, 2),
                'total_hours': round(total_hours, 1),
                'schedule_hours': round(schedule_hours, 1),
                'ai_active_days': ai_active_days,
                'period_start': record.get('统计周期开始'),
                'period_end': record.get('统计周期结束')
            })
        
        logger.info(f"📈 筛选结果: 原始数据 {len(workload_data)} 条 -> 保留 {filtered_count} 条 -> 跳过 {skipped_count} 条 -> 最终返回 {len(workload_stats)} 条")
        
        # 如果没有筛选出任何数据，记录详细信息
        if len(workload_stats) == 0:
            logger.warning("⚠️ 没有筛选出任何工作量数据，请检查:")
            logger.warning(f"   - 筛选时间范围: {start_dt} 到 {end_dt}")
            logger.warning(f"   - 数据时间字段是否正确")
            logger.warning(f"   - 时间格式是否能正确解析")
            
            # 输出前几条记录的时间信息用于调试
            for i in range(min(5, len(workload_data))):
                record = workload_data[i]
                logger.warning(f"   - 样本记录 {i}: 开始={record.get('统计周期开始')}, 结束={record.get('统计周期结束')}")
                # 尝试解析时间看看是否有问题
                start_parsed = parse_workload_date(record.get('统计周期开始'))
                end_parsed = parse_workload_date(record.get('统计周期结束'))
                logger.warning(f"   - 解析结果 {i}: 开始={start_parsed}, 结束={end_parsed}")
        
        return workload_stats
        
    except Exception as e:
        logger.error(f"❌ 计算工作量统计数据失败: {e}", exc_info=True)
        return []
# 修改 calculate_delivery_stats 函数
# 从第130行开始

def calculate_delivery_stats(start_date, end_date):
    """根据时间筛选计算交付统计数据"""
    try:
        start_dt = datetime.strptime(start_date, '%Y-%m-%d').date()
        end_dt = datetime.strptime(end_date, '%Y-%m-%d').date()
        
        # 筛选新建时间在指定时间范围内的需求
        new_requirements_data = []
        # 筛选实际上线时间在指定时间范围内的需求
        delivered_requirements_data = []
        
        for record in delivery_data:
            create_time = None
            online_time = None
            
            # 尝试获取创建时间
            if '创建时间' in record and record['创建时间']:
                create_time = parse_date_to_date(record['创建时间'])
            
            # 尝试获取实际上线时间
            if '实际上线时间' in record and record['实际上线时间']:
                online_time = parse_date_to_date(record['实际上线时间'])
            
            # 新增需求：按创建时间筛选
            if create_time and start_dt <= create_time <= end_dt:
                new_requirements_data.append(record)
            
            # 需求交付：按实际上线时间筛选
            if online_time and start_dt <= online_time <= end_dt:
                delivered_requirements_data.append(record)
        
        # 计算统计指标
        new_requirements = len(new_requirements_data)
        delivered_requirements = len(delivered_requirements_data)
        
        # 计算吞吐率（交付需求占新增需求的比例）
        throughput_rate = (delivered_requirements / new_requirements * 100) if new_requirements > 0 else 0
        
        return {
            'new_requirements': new_requirements,
            'delivered_requirements': delivered_requirements,
            'throughput_rate': throughput_rate
        }
        
    except Exception as e:
        print(f"计算交付统计数据失败: {e}")
        return {
            'new_requirements': 0,
            'delivered_requirements': 0,
            'throughput_rate': 0
        }

# 修改 calculate_ft_project_stats 函数
# 从第190行开始

def calculate_ft_project_stats(start_date, end_date):
    """根据时间筛选计算FT项目统计数据"""
    try:
        start_dt = datetime.strptime(start_date, '%Y-%m-%d').date()
        end_dt = datetime.strptime(end_date, '%Y-%m-%d').date()
        
        # 按FT项目分组统计
        ft_projects = {}
        
        for record in delivery_data:
            create_time = None
            online_time = None
            
            # 获取创建时间
            if '创建时间' in record and record['创建时间']:
                create_time = parse_date_to_date(record['创建时间'])
            
            # 获取实际上线时间
            if '实际上线时间' in record and record['实际上线时间']:
                online_time = parse_date_to_date(record['实际上线时间'])
            
            # 获取FT项目名称
            ft_name = record.get('业务线', '未知项目')
            
            if ft_name not in ft_projects:
                ft_projects[ft_name] = {
                    'project_name': ft_name,
                    'new_requirements': 0,
                    'delivered_requirements': 0
                }
            
            # 新增需求：按创建时间统计
            if create_time and start_dt <= create_time <= end_dt:
                ft_projects[ft_name]['new_requirements'] += 1
            
            # 需求交付：按实际上线时间统计
            if online_time and start_dt <= online_time <= end_dt:
                ft_projects[ft_name]['delivered_requirements'] += 1
        
        # 计算每个项目的吞吐率
        result = []
        for project in ft_projects.values():
            throughput_rate = (project['delivered_requirements'] / project['new_requirements'] * 100) if project['new_requirements'] > 0 else 0
            project['throughput_rate'] = round(throughput_rate, 1)
            result.append(project)
        
        return result
        
    except Exception as e:
        print(f"计算FT项目统计数据失败: {e}")
        return []
def calculate_defect_stats(start_date, end_date):
    """计算缺陷统计数据，严格按照时间筛选"""
    try:
        start_dt = datetime.strptime(start_date, '%Y-%m-%d').date()
        end_dt = datetime.strptime(end_date, '%Y-%m-%d').date()
        
        print(f"[INFO] 缺陷统计时间范围: {start_date} 到 {end_date}")
        print(f"[INFO] 原始缺陷数据量: {len(defect_data)}")
        
        # 检查数据字段
        if defect_data:
            sample_record = defect_data[0]
            print(f"📋 缺陷数据字段: {list(sample_record.keys())}")
            # 检查多种可能的时间字段
            possible_time_fields = ['创建时间', '缺陷创建时间', '创建日期', '发现时间']
            for field in possible_time_fields:
                if field in sample_record:
                    print(f"🕐 找到时间字段 {field}: {sample_record.get(field)}")
        
        # 筛选在指定时间范围内的缺陷数据
        filtered_defects = []
        valid_time_count = 0
        skipped_count = 0
        
        for record in defect_data:
            # 尝试获取创建时间，支持多种字段名
            create_time = None
            time_field_used = None
            
            # 按优先级检查时间字段
            time_fields = ['创建时间', '缺陷创建时间', '创建日期', '发现时间']
            for field in time_fields:
                if field in record and record[field]:
                    try:
                        create_time = parse_date_to_date(record[field])
                        if create_time:  # 只有成功解析才使用
                            time_field_used = field
                            valid_time_count += 1
                            break
                    except Exception as e:
                        print(f"[WARNING] 解析时间字段 {field} 失败: {record[field]}, 错误: {e}")
                        continue
            
            # 如果找到有效时间且在范围内，则加入筛选结果
            if create_time:
                # 详细的时间比较调试
                is_in_range = start_dt <= create_time <= end_dt
                if len(filtered_defects) < 10:  # 只为前10条记录打印详细信息
                    print(f"[DEBUG] 时间比较: {start_dt} <= {create_time} <= {end_dt} = {is_in_range}")
                
                if is_in_range:
                    filtered_defects.append(record)
                    # 调试信息
                    if len(filtered_defects) <= 5:  # 只打印前5条调试信息
                        print(f"[INFO] 缺陷已筛选: ID={record.get('缺陷ID', record.get('ID', 'N/A'))}, 时间: {create_time} (字段: {time_field_used})")
                else:
                    skipped_count += 1
                    if skipped_count <= 3:  # 只打印前3条跳过的记录
                        print(f"[INFO] 缺陷已跳过: ID={record.get('缺陷ID', record.get('ID', 'N/A'))}, 时间: {create_time} (不在范围内)")
            else:
                skipped_count += 1
        
        print(f"[INFO] 有效时间字段数量: {valid_time_count}")
        print(f"[INFO] 跳过的记录数量: {skipped_count}")
        print(f"[INFO] 筛选后缺陷数据量: {len(filtered_defects)}")
        
        if not filtered_defects:
            print("[WARNING] 警告：筛选后无数据，可能原因：")
            print("   1. 时间字段名称不匹配")
            print("   2. 时间格式不正确")
            print("   3. 选择的时间范围内无数据")
            print("   4. 时间字段为空值")
            
            # 提供数据样本供调试
            if defect_data:
                sample = defect_data[0]
                print("[DEBUG] 数据样本（用于调试）:")
                for key, value in sample.items():
                    if '时间' in key or '日期' in key:
                        print(f"   {key}: {value}")
            
            return {
                'total_defects': 0,
                'status_stats': {},
                'priority_stats': {},
                'creator_stats': {},
                'ft_defect_stats': [],
                'defect_list': []
            }
        
        # 使用已筛选的 filtered_defects 进行所有统计计算
        print(f"[INFO] 开始统计计算，基于已筛选的 {len(filtered_defects)} 条缺陷记录")
        
        # 按状态分组统计（基于筛选后的数据）
        status_stats = {}
        for defect in filtered_defects:
            status = str(defect.get('状态', defect.get('缺陷状态', '未知')))
            status_stats[status] = status_stats.get(status, 0) + 1
        
        # 按优先级分组统计（基于筛选后的数据）
        priority_stats = {}
        for defect in filtered_defects:
            priority = str(defect.get('优先级', defect.get('缺陷优先级', '未知')))
            priority_stats[priority] = priority_stats.get(priority, 0) + 1
        
        # 按业务线分组统计（基于筛选后的数据）
        ft_defect_stats = {}
        for defect in filtered_defects:
            ft_name = str(defect.get('业务线', defect.get('所属业务线', defect.get('项目', '未知项目'))))
            if ft_name not in ft_defect_stats:
                ft_defect_stats[ft_name] = {
                    'ft_name': ft_name,
                    'total_defects': 0,
                    'open_defects': 0,
                    'closed_defects': 0,
                    'defects_by_status': {}
                }
            
            ft_defect_stats[ft_name]['total_defects'] += 1
            
            # 统计开启和关闭的缺陷
            status = str(defect.get('状态', defect.get('缺陷状态', '未知')))
            if status.lower() in ['已关闭', 'closed', '已解决', 'resolved', '已完成', '关闭']:
                ft_defect_stats[ft_name]['closed_defects'] += 1
            else:
                ft_defect_stats[ft_name]['open_defects'] += 1
            
            # 按状态统计
            ft_defect_stats[ft_name]['defects_by_status'][status] = \
                ft_defect_stats[ft_name]['defects_by_status'].get(status, 0) + 1
        
        # 按创建人分组统计（基于筛选后的数据）
        creator_stats = {}
        for defect in filtered_defects:
            # 支持多种创建人字段名
            creator = str(defect.get('创建人', 
                         defect.get('缺陷创建人', 
                         defect.get('创建者', 
                         defect.get('提交人', '未知')))))
            creator_stats[creator] = creator_stats.get(creator, 0) + 1
        
        print(f"[INFO] 业务线统计结果: {len(ft_defect_stats)}个业务线")
        print(f"[INFO] 创建人统计结果: {len(creator_stats)}个创建人")
        
        # 打印详细统计信息供调试
        print(f"[INFO] 状态分布: {status_stats}")
        print(f"[INFO] 优先级分布: {priority_stats}")
        
        # 准备缺陷列表（只显示最近50个）
        defect_list = []
        for defect in filtered_defects[-50:]:  # 只取最近50个
            defect_info = {
                'defect_id': str(defect.get('缺陷ID', defect.get('ID', defect.get('编号', '未知')))),
                'title': str(defect.get('标题', defect.get('缺陷标题', defect.get('描述', '无标题')))),
                'status': str(defect.get('状态', defect.get('缺陷状态', '未知'))),
                'priority': str(defect.get('优先级', defect.get('缺陷优先级', '未知'))),
                'ft_name': str(defect.get('业务线', defect.get('所属业务线', defect.get('项目', '未知项目')))),
                'assignee': str(defect.get('负责人', defect.get('处理人', defect.get('指派给', '未分配')))),
                'create_time': str(defect.get('创建时间', defect.get('缺陷创建时间', defect.get('创建日期', ''))))
            }
            defect_list.append(defect_info)
        
        result = {
            'total_defects': len(filtered_defects),
            'status_stats': status_stats,
            'priority_stats': priority_stats,
            'creator_stats': creator_stats,
            'ft_defect_stats': list(ft_defect_stats.values()),
            'defect_list': defect_list
        }
        
        print(f"[INFO] 最终返回结果: 总缺陷{result['total_defects']}, 业务线{len(result['ft_defect_stats'])}, 创建人{len(result['creator_stats'])}")
        print(f"[INFO] 确认：所有统计都基于时间筛选后的 {len(filtered_defects)} 条记录")
        return result
        
    except Exception as e:
        print(f"[ERROR] 计算缺陷统计数据失败: {e}")
        import traceback
        traceback.print_exc()
        return {
            'total_defects': 0,
            'status_stats': {},
            'priority_stats': {},
            'creator_stats': {},
            'ft_defect_stats': [],
            'defect_list': []
        }
# 修改 calculate_delayed_requirements 函数
# 从第207行开始

def calculate_delayed_requirements(start_date, end_date):
    """根据时间筛选计算延期需求数据"""
    try:
        start_dt = datetime.strptime(start_date, '%Y-%m-%d').date()
        end_dt = datetime.strptime(end_date, '%Y-%m-%d').date()
        
        delayed_requirements = []
        
        for record in delivery_data:
            # 获取所有相关时间字段
            plan_online_time = None
            actual_online_time = None
            plan_test_time = None
            actual_test_time = None
            
            if '计划上线时间' in record and record['计划上线时间']:
                plan_online_time = parse_date_to_date(record['计划上线时间'])
            
            if '实际上线时间' in record and record['实际上线时间']:
                actual_online_time = parse_date_to_date(record['实际上线时间'])
                
            if '计划提测时间' in record and record['计划提测时间']:
                plan_test_time = parse_date_to_date(record['计划提测时间'])
            
            if '实际提测时间' in record and record['实际提测时间']:
                actual_test_time = parse_date_to_date(record['实际提测时间'])
            
            # 新增严格筛选：实际上线时间必须存在且在时间范围内
            if not actual_online_time:
                continue  # 实际上线时间为空，跳过
                
            if not (start_dt <= actual_online_time <= end_dt):
                continue  # 实际上线时间不在范围内，跳过
            
            # 检查是否有其他相关时间字段在时间范围内（保持原有逻辑）
            has_time_in_range = False
            
            if plan_online_time and start_dt <= plan_online_time <= end_dt:
                has_time_in_range = True
            
            if actual_online_time and start_dt <= actual_online_time <= end_dt:
                has_time_in_range = True
            
            if plan_test_time and start_dt <= plan_test_time <= end_dt:
                has_time_in_range = True
            
            if actual_test_time and start_dt <= actual_test_time <= end_dt:
                has_time_in_range = True
            
            if not has_time_in_range:
                continue
            
            # 延期判断逻辑（保持不变）
            is_delayed = False
            delay_reason = []
            
            # 1. 检查实际上线时间延期
            if plan_online_time and plan_online_time:
                if actual_online_time and actual_online_time:
                    if actual_online_time > plan_online_time:
                        is_delayed = True
                        delay_reason.append("上线延期")
                else:
                    is_delayed = True
                    delay_reason.append("未填写实际上线时间")
            
            # 2. 检查实际提测时间延期
            if plan_test_time and plan_test_time:
                if actual_test_time and actual_test_time:
                    if actual_test_time > plan_test_time:
                        is_delayed = True
                        delay_reason.append("提测延期")
                else:
                    is_delayed = True
                    delay_reason.append("未填写实际提测时间")
            
            # 3. 特殊情况处理
            if (plan_online_time and plan_online_time and 
                (not record.get('实际上线时间') or str(record.get('实际上线时间')).strip() == '')):
                is_delayed = True
                delay_reason.append("计划上线但未完成")
            
            if is_delayed:
                # 尝试获取需求名称
                requirement_name = '未知需求'
                for col in ['需求名称', '需求', '需求名', '需求标题', '需求描述']:
                    if col in record and record[col] and pd.notna(record[col]):
                        requirement_name = str(record[col])
                        break
                
                delayed_requirements.append({
                    'requirement_name': requirement_name,
                    'ft_name': str(record.get('业务线', '未知项目')),
                    'system': str(record.get('系统', '未知系统')),
                    'pm': str(record.get('PM', '')),
                    'rd': str(record.get('RD', '')),
                    'fe': str(record.get('FE', '')),
                    'qa': str(record.get('QA', '')),
                    'plan_online_time': str(record.get('计划上线时间', '')),
                    'actual_online_time': str(record.get('实际上线时间', '')),
                    'plan_test_time': str(record.get('计划提测时间', '')),
                    'actual_test_time': str(record.get('实际提测时间', '')),
                    'delay_reason': '、'.join(list(set(delay_reason))) if delay_reason else '延期'
                })
        
        return delayed_requirements
        
    except Exception as e:
        print(f"计算延期需求数据失败: {e}")
        import traceback
        traceback.print_exc()
        return []
@app.route("/api/performance", methods=["GET"])
def api_performance():
    """性能数据API - 增强版本"""
    perf_logger = logging.getLogger('performance')
    start_time = datetime.now()
    
    try:
        start_date = request.args.get('start_date')
        end_date = request.args.get('end_date')
        
        logger.info(f"📊 收到性能数据请求 - 开始: {start_date}, 结束: {end_date}")
        
        # 验证日期参数
        if not start_date or not end_date:
            logger.warning("⚠️ 缺少日期参数")
            return jsonify({'error': '缺少日期参数'}), 400
        
        # 计算各项统计数据
        logger.info("🔢 开始计算统计数据...")
        
        # 交付统计
        delivery_start = datetime.now()
        stats = calculate_delivery_stats(start_date, end_date)
        delivery_time = (datetime.now() - delivery_start).total_seconds()
        perf_logger.info(f"⏱️ 交付统计耗时: {delivery_time:.3f}秒")
        
        # FT项目统计
        ft_start = datetime.now()
        ft_projects = calculate_ft_project_stats(start_date, end_date)
        ft_time = (datetime.now() - ft_start).total_seconds()
        perf_logger.info(f"⏱️ FT项目统计耗时: {ft_time:.3f}秒")
        
        # 延期需求统计
        delayed_start = datetime.now()
        delayed_requirements = calculate_delayed_requirements(start_date, end_date)
        delayed_time = (datetime.now() - delayed_start).total_seconds()
        perf_logger.info(f"⏱️ 延期需求统计耗时: {delayed_time:.3f}秒")
        
        # 工作量统计
        workload_start = datetime.now()
        workload_stats = calculate_workload_stats(start_date, end_date)
        workload_time = (datetime.now() - workload_start).total_seconds()
        perf_logger.info(f"⏱️ 工作量统计耗时: {workload_time:.3f}秒")
        
        # 缺陷统计
        defect_start = datetime.now()
        defect_stats = calculate_defect_stats(start_date, end_date)
        defect_time = (datetime.now() - defect_start).total_seconds()
        perf_logger.info(f"⏱️ 缺陷统计耗时: {defect_time:.3f}秒")
        
        # 构建响应
        response_data = {
            "delivery_stats": stats,
            "ft_projects": ft_projects,
            "delayed_requirements": delayed_requirements,
            "workload_stats": workload_stats,
            "defect_stats": defect_stats
        }
        
        # 总性能日志
        total_time = (datetime.now() - start_time).total_seconds()
        perf_logger.info(f"✅ API请求完成 - 总耗时: {total_time:.3f}秒, "
                        f"交付: {delivery_time:.3f}s, "
                        f"FT项目: {ft_time:.3f}s, "
                        f"延期需求: {delayed_time:.3f}s, "
                        f"工作量: {workload_time:.3f}s, "
                        f"缺陷: {defect_time:.3f}s")
        
        logger.info(f"📤 返回性能数据 - 交付: {stats}, "
                   f"FT项目: {len(ft_projects)}个, "
                   f"延期需求: {len(delayed_requirements)}个, "
                   f"工作量: {len(workload_stats)}条记录, "
                   f"缺陷: {defect_stats.get('total_defects', 0)}个")
        
        return jsonify(response_data)
        
    except Exception as e:
        logger.error(f"❌ 性能API处理失败: {e}", exc_info=True)
        perf_logger.error(f"❌ API请求异常 - 耗时: {(datetime.now() - start_time).total_seconds():.3f}秒")
        return jsonify({'error': str(e)}), 500

# 增强的文件上传处理函数
@app.route('/upload', methods=['POST'])
def upload_file():
    """文件上传接口 - 增强版本"""
    logger.info("[INFO] 收到文件上传请求")
    
    try:
        if 'file' not in request.files:
            logger.warning("[WARNING] 上传请求中没有文件")
            return jsonify({'error': '没有文件'}), 400
            
        file = request.files['file']
        file_type = request.form.get('type', 'unknown')
        
        logger.info(f"[INFO] 文件信息 - 名称: {file.filename}, 类型: {file_type}, 大小: {len(file.read()) if file else 0} bytes")
        file.seek(0)  # 重置文件指针
        
        if file.filename == '':
            logger.warning("[WARNING] 文件名为空")
            return jsonify({'error': '文件名为空'}), 400
            
        if not file:
            logger.warning("[WARNING] 文件对象为空")
            return jsonify({'error': '文件对象为空'}), 400
            
        # 保存文件
        filename = secure_filename(file.filename)
        upload_path = os.path.join(app.config['UPLOAD_FOLDER'], file_type, filename)
        
        logger.info(f"[INFO] 保存文件到: {upload_path}")
        file.save(upload_path)
        
        # 处理文件
        logger.info(f"[INFO] 开始处理 {file_type} 类型文件")
        result = process_file_by_type(upload_path, file_type)
        
        if result['success']:
            logger.info(f"[INFO] 文件处理成功 - 处理了 {result.get('count', 0)} 条记录")
        else:
            logger.error(f"[ERROR] 文件处理失败 - 错误: {result.get('error', '未知错误')}")
            
        return jsonify(result)
        
    except Exception as e:
        logger.error(f"[ERROR] 文件上传处理失败: {e}", exc_info=True)
        return jsonify({'error': str(e)}), 500

# API上传路由 - 为前端提供兼容的API端点
@app.route('/api/upload/<data_type>', methods=['POST'])
def api_upload_file(data_type):
    """API文件上传接口"""
    logger.info(f"[INFO] 收到API文件上传请求 - 类型: {data_type}")
    
    try:
        if 'file' not in request.files:
            logger.warning("[WARNING] 上传请求中没有文件")
            return jsonify({'success': False, 'message': '没有文件'}), 400
            
        file = request.files['file']
        
        if file.filename == '':
            logger.warning("[WARNING] 文件名为空")
            return jsonify({'success': False, 'message': '文件名为空'}), 400
            
        if not file:
            logger.warning("[WARNING] 文件对象为空")
            return jsonify({'success': False, 'message': '文件对象为空'}), 400
            
        # 保存文件
        filename = secure_filename(file.filename)
        upload_path = os.path.join(app.config['UPLOAD_FOLDER'], data_type, filename)
        
        logger.info(f"[INFO] 保存文件到: {upload_path}")
        file.save(upload_path)
        
        # 处理文件
        logger.info(f"[INFO] 开始处理 {data_type} 类型文件")
        count = process_file_by_type(upload_path, data_type)
        
        logger.info(f"[INFO] 文件处理完成 - 处理了 {count} 条记录")
        return jsonify({
            'success': True, 
            'message': '文件上传并处理成功',
            'rows': count
        })
        
    except Exception as e:
        logger.error(f"[ERROR] API文件上传处理失败: {e}", exc_info=True)
        return jsonify({
            'success': False, 
            'message': f'处理失败: {str(e)}'
        }), 500

# 修改文件处理函数
# 在文件开头添加（约第50行附近）
# 存储解析后的交付数据、工作量数据和缺陷数据
delivery_data = []
workload_data = []
defect_data = []  # 新增：缺陷数据存储

# 修改工作量数据处理部分
# 修改后的代码（删除第550行的冗余return）
# 完全修复后的 process_file_by_type 函数
# 替换现有的不完整实现

def process_file_by_type(filepath, data_type):
    try:
        if data_type == 'delivery':
            # 处理交付数据
            try:
                if filepath.endswith('.xls'):
                    df = pd.read_excel(filepath, engine='xlrd')
                else:
                    df = pd.read_excel(filepath, engine='openpyxl')
                
                # 清空之前的交付数据
                delivery_data.clear()
                
                # 标准化列名
                df.columns = df.columns.str.strip()
                
                # 处理交付数据
                for _, row in df.iterrows():
                    record = {}
                    for col in df.columns:
                        record[col] = row[col] if pd.notna(row[col]) else None
                    delivery_data.append(record)
                
                return len(delivery_data)
                
            except Exception as e:
                logger.error(f"交付文件处理失败: {e}")
                return 0
                
        elif data_type == 'workload':
            # 支持.xls和.xlsx格式
            try:
                logger.info(f"🚀 开始处理工作量文件: {filepath}")
                
                if filepath.endswith('.xls'):
                    df = pd.read_excel(filepath, engine='xlrd')
                else:
                    df = pd.read_excel(filepath, engine='openpyxl')
                
                logger.info(f"📊 工作量文件读取成功: {df.shape}")
                
                # 清空之前的工作量数据
                workload_data.clear()
                
                # 标准化列名
                df.columns = df.columns.str.strip()
                
                # 强制输出所有列名 - 使用print确保能看到
                print(f"[CRITICAL] Excel文件所有列名: {list(df.columns)}")
                logger.error(f"[CRITICAL] Excel文件所有列名: {list(df.columns)}")  # 使用error级别确保输出
                
                # 扩展的列名映射 - 根据实际Excel结构调整
                column_mapping = {
                    '人员名称': ['人员名称', '用户名称', '姓名', '成员名称', '人员姓名', 'name', 'Name', '员工姓名', '开发人员'],
                    '饱和度': ['饱和度', '工作饱和度', '负载率', '饱和度(%)', 'saturation', 'Saturation'],
                    '代码当量': ['代码当量', '代码量', '代码贡献', '当量', 'code_equivalent', 'Code'],
                    '总工时': ['总工时', '工时', '工作时长', '总时长(小时)', 'hours', 'Hours', '总时长', '排期工时(pd)', '工作时间'],
                    '排期工时': ['排期工时', '计划工时', '预期工时', '排期时长', 'schedule_hours', 'Schedule Hours', '排期工时(pd)'],
                    'AI活跃天数': ['AI活跃天数', 'AI使用天数', '活跃天数', 'AI天数', 'ai_days', 'AI Days'],
                    '统计周期开始': [
                        '统计周期开始', '开始日期', '周期开始', 'start_date', 'Start Date',
                        '开始时间', '起始日期', '统计开始', '周期起始', '开始', '起始时间',
                        '统计周期开始时间', '周期开始日期', 'StartDate', 'start_time',
                        '统计周期'  # 添加实际的字段名
                    ],
                    '统计周期结束': [
                        '统计周期结束', '结束日期', '周期结束', 'end_date', 'End Date',
                        '结束时间', '终止日期', '统计结束', '周期终止', '结束', '终止时间',
                        '统计周期结束时间', '周期结束日期', 'EndDate', 'end_time'
                        # 注意：不在这里添加'统计周期'，避免重复映射
                    ]
                }
                
                # 创建反向映射
                reverse_mapping = {}
                for standard_name, possible_names in column_mapping.items():
                    found = False
                    for col_name in possible_names:
                        if col_name in df.columns:
                            reverse_mapping[standard_name] = col_name
                            found = True
                            break
                    if not found:
                        print(f"[WARNING] 未找到字段 '{standard_name}' 的匹配列名")
                        logger.warning(f"⚠️ 未找到字段 '{standard_name}' 的匹配列名")

                print(f"[CRITICAL] 成功映射的列名: {reverse_mapping}")
                logger.error(f"[CRITICAL] 成功映射的列名: {reverse_mapping}")
                
                # 特殊处理：如果只找到统计周期开始，将其同时用作结束时间
                if '统计周期开始' in reverse_mapping and '统计周期结束' not in reverse_mapping:
                    if reverse_mapping['统计周期开始'] == '统计周期':
                        reverse_mapping['统计周期结束'] = '统计周期'
                        print(f"[INFO] 将'统计周期'字段同时用作开始和结束时间")
                        logger.info(f"✅ 将'统计周期'字段同时用作开始和结束时间")
                
                # 如果时间字段没有找到，尝试模糊匹配
                if '统计周期开始' not in reverse_mapping or '统计周期结束' not in reverse_mapping:
                    print("[WARNING] 时间字段映射失败，尝试模糊匹配...")
                    logger.warning("⚠️ 时间字段映射失败，尝试模糊匹配...")
                    
                    for col in df.columns:
                        col_lower = col.lower()
                        # 匹配开始时间字段
                        if '统计周期开始' not in reverse_mapping:
                            if any(keyword in col_lower for keyword in ['开始', 'start', '起始', '开端']):
                                reverse_mapping['统计周期开始'] = col
                                print(f"[INFO] 模糊匹配到开始时间字段: {col}")
                                logger.info(f"✅ 模糊匹配到开始时间字段: {col}")
                        
                        # 匹配结束时间字段
                        if '统计周期结束' not in reverse_mapping:
                            if any(keyword in col_lower for keyword in ['结束', 'end', '终止', '结尾']):
                                reverse_mapping['统计周期结束'] = col
                                print(f"[INFO] 模糊匹配到结束时间字段: {col}")
                                logger.info(f"✅ 模糊匹配到结束时间字段: {col}")
                
                # 如果还是没找到，直接使用原始列名
                if '统计周期开始' not in reverse_mapping or '统计周期结束' not in reverse_mapping:
                    print("[ERROR] 无法找到时间字段，将保存所有原始列名用于调试")
                    logger.error("❌ 无法找到时间字段，将保存所有原始列名用于调试")
                    
                    # 输出前几行的所有数据用于调试
                    for i, (_, row) in enumerate(df.head(3).iterrows()):
                        print(f"[DEBUG] 原始数据行 {i}: {dict(row)}")
                        logger.error(f"[DEBUG] 原始数据行 {i}: {dict(row)}")
                
                # 处理每一行数据
                for i, (_, row) in enumerate(df.iterrows()):
                    record = {}
                    
                    # 首先保存所有原始列数据
                    for col in df.columns:
                        record[col] = row[col] if pd.notna(row[col]) else None
                    
                    # 然后进行标准化映射
                    for standard_name, actual_col in reverse_mapping.items():
                        if actual_col in df.columns:
                            raw_value = row[actual_col] if pd.notna(row[actual_col]) else None
                            
                            # 数据类型转换
                            if standard_name == '饱和度':
                                try:
                                    saturation_str = str(raw_value).replace('%', '') if raw_value else '0'
                                    record[standard_name] = float(saturation_str)
                                except:
                                    record[standard_name] = 0
                            elif standard_name in ['总工时', '代码当量', '排期工时']:
                                try:
                                    record[standard_name] = float(raw_value) if raw_value else 0
                                except:
                                    record[standard_name] = 0
                            elif standard_name == 'AI活跃天数':
                                try:
                                    record[standard_name] = int(float(str(raw_value))) if raw_value else 0
                                except:
                                    record[standard_name] = 0
                            elif standard_name in ['统计周期开始', '统计周期结束']:
                                # 特殊处理时间字段
                                if raw_value:
                                    if isinstance(raw_value, pd.Timestamp):
                                        record[standard_name] = raw_value.strftime('%Y-%m-%d')
                                    elif isinstance(raw_value, datetime):
                                        record[standard_name] = raw_value.strftime('%Y-%m-%d')
                                    else:
                                        record[standard_name] = str(raw_value)
                                else:
                                    record[standard_name] = None
                            else:
                                record[standard_name] = str(raw_value) if raw_value else ''
                    
                    # 确保包含所有必要字段
                    record.setdefault('人员名称', '')
                    record.setdefault('饱和度', 0)
                    record.setdefault('代码当量', 0)
                    record.setdefault('总工时', 0)
                    record.setdefault('AI活跃天数', 0)
                    record.setdefault('统计周期开始', None)
                    record.setdefault('统计周期结束', None)
                    record.setdefault('排期工时', 0)
                    
                    workload_data.append(record)
                
                print(f"[INFO] 工作量数据处理完成: {len(workload_data)} 条记录")
                logger.info(f"✅ 工作量数据处理完成: {len(workload_data)} 条记录")
                
                # 显示前几条记录的时间字段用于调试
                for i in range(min(3, len(workload_data))):
                    record = workload_data[i]
                    print(f"[DEBUG] 处理后记录 {i}: 人员={record.get('人员名称')}, 开始={record.get('统计周期开始')}, 结束={record.get('统计周期结束')}")
                    logger.error(f"[DEBUG] 处理后记录 {i}: 人员={record.get('人员名称')}, 开始={record.get('统计周期开始')}, 结束={record.get('统计周期结束')}")
                
                # 如果开始和结束时间相同（来自同一个统计周期字段），记录这个情况
                if len(workload_data) > 0:
                    sample_record = workload_data[0]
                    if (sample_record.get('统计周期开始') == sample_record.get('统计周期结束') and 
                        sample_record.get('统计周期开始') is not None):
                        print(f"[INFO] 检测到使用单一统计周期字段: {sample_record.get('统计周期开始')}")
                        logger.info(f"✅ 检测到使用单一统计周期字段: {sample_record.get('统计周期开始')}")
                
                return len(workload_data)
                
            except Exception as e:
                print(f"[ERROR] 工作量文件处理失败: {e}")
                logger.error(f"❌ 工作量文件处理失败: {e}", exc_info=True)
                return 0
                
        elif data_type == 'defect':
            # 处理缺陷数据
            try:
                if filepath.endswith('.xls'):
                    df = pd.read_excel(filepath, engine='xlrd')
                else:
                    df = pd.read_excel(filepath, engine='openpyxl')
                
                # 清空之前的缺陷数据
                defect_data.clear()
                
                # 标准化列名
                df.columns = df.columns.str.strip()
                
                # 处理缺陷数据
                for _, row in df.iterrows():
                    record = {}
                    for col in df.columns:
                        record[col] = row[col] if pd.notna(row[col]) else None
                    defect_data.append(record)
                
                return len(defect_data)
                
            except Exception as e:
                logger.error(f"缺陷文件处理失败: {e}")
                return 0
                
        else:
            return 0
            
    except Exception as e:
        print(f"[ERROR] 文件处理失败: {e}")
        logger.error(f"文件处理失败: {e}", exc_info=True)
        return 0

def get_type_name(data_type):
    """获取数据类型的中文名称"""
    type_names = {
        'delivery': '交付数据',
        'workload': '工作量数据',
        'defect': '缺陷数据'
    }
    return type_names.get(data_type, '数据')


def allowed_file(filename):
    return '.' in filename and filename.rsplit('.', 1)[1].lower() in {'xlsx', 'xls'}

@app.route("/api/ai-analyze", methods=["POST"])
def ai_analyze():
    """调用SiliconFlow API进行AI分析，支持重试机制"""
    import time
    
    try:
        data = request.json
        
        # 构建分析提示
        prompt = f"""
        请作为效能分析专家，对以下团队效能数据进行全面分析：
        
        交付情况：
        - 需求吞吐率：{data.get('delivery_stats', {}).get('throughput_rate', 0)}%
        - 新增需求数量：{data.get('delivery_stats', {}).get('new_requirements', 0)}
        - 需求交付数量：{data.get('delivery_stats', {}).get('delivered_requirements', 0)}
        
        FT项目明细：
        {json.dumps(data.get('ft_projects', []), ensure_ascii=False, indent=2)}
        
        延期需求：
        {json.dumps(data.get('delayed_requirements', []), ensure_ascii=False, indent=2)}
        
        工作量情况：
        {json.dumps(data.get('workload_stats', []), ensure_ascii=False, indent=2)}
        
        请提供：
        1. 关键发现和趋势分析
        2. 存在的问题和风险
        3. 改进建议和优化方向
        4. 团队协作效率评估
        5. 资源分配优化建议
        
        请用中文回答，保持专业但易于理解。
        """
        
        # 调用SiliconFlow API
        api_key = "sk-cgeodmtqtwhrrcaqppopyblkvfwcggowlqryvqwyhoivfiub"
        headers = {
            "Authorization": f"Bearer {api_key}",
            "Content-Type": "application/json"
        }
        
        payload = {
            "model": "Qwen/Qwen3-8B",
            "messages": [
                {"role": "user", "content": prompt}
            ],
            "max_tokens": 2000,
            "temperature": 0.7
        }
        
        # 重试机制配置
        max_retries = 3
        retry_delay = 1  # 初始延迟1秒
        
        for attempt in range(max_retries):
            try:
                print(f"[INFO] 正在进行AI分析，尝试第 {attempt + 1}/{max_retries} 次...")
                
                response = requests.post(
                    "https://api.siliconflow.cn/v1/chat/completions",
                    headers=headers,
                    json=payload,
                    timeout=300  # 增加到300秒超时
                )
                
                if response.status_code == 200:
                    result = response.json()
                    analysis = result['choices'][0]['message']['content']
                    
                    print("[INFO] AI分析完成")
                    return jsonify({
                        "success": True,
                        "analysis": {
                            "summary": analysis,
                            "timestamp": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
                            "data_summary": {
                                "total_projects": len(data.get('ft_projects', [])),
                                "delayed_count": len(data.get('delayed_requirements', [])),
                                "workload_count": len(data.get('workload_stats', []))
                            },
                            "api_calls": attempt + 1
                        }
                    })
                else:
                    error_msg = f"API调用失败: {response.status_code}"
                    print(f"[WARNING] {error_msg}")
                    
                    if attempt < max_retries - 1:
                        print(f"[INFO] 等待 {retry_delay} 秒后重试...")
                        time.sleep(retry_delay)
                        retry_delay *= 2  # 指数退避
                    else:
                        return jsonify({
                            "success": False,
                            "message": error_msg,
                            "retry_count": attempt + 1
                        })
                        
            except requests.exceptions.Timeout:
                print(f"[WARNING] 第 {attempt + 1} 次请求超时")
                if attempt < max_retries - 1:
                    print(f"[INFO] 等待 {retry_delay} 秒后重试...")
                    time.sleep(retry_delay)
                    retry_delay *= 2
                else:
                    return jsonify({
                        "success": False,
                        "message": "API调用超时，请稍后重试",
                        "retry_count": attempt + 1
                    })
                    
            except requests.exceptions.ConnectionError as e:
                print(f"[ERROR] 连接错误: {e}")
                if attempt < max_retries - 1:
                    print(f"[INFO] 等待 {retry_delay} 秒后重试...")
                    time.sleep(retry_delay)
                    retry_delay *= 2
                else:
                    return jsonify({
                        "success": False,
                        "message": "网络连接失败，请检查网络设置",
                        "retry_count": attempt + 1
                    })
                    
    except Exception as e:
        print(f"[ERROR] AI分析失败: {e}")
        return jsonify({
            "success": False,
            "message": f"分析过程出错: {str(e)}"
        })
@app.route("/")
def index():
    """主页路由"""
    return render_template("base.html")

@app.route("/performance")
def performance():
    """性能监控页面路由"""
    return render_template("performance.html")

@app.route("/settings")
def settings():
    """设置页面路由"""
    return render_template("settings.html")

@app.route("/ai_report")
def ai_report():
    """AI报告页面路由"""
    return render_template("ai_report.html")


def load_latest_files():
    """项目启动时读取 uploads 中最新的交付 / 工作量 / 缺陷文件"""
    for data_type in ["delivery", "workload", "defect"]:
        type_folder = os.path.join(UPLOAD_FOLDER, data_type)
        if not os.path.isdir(type_folder):
            continue
        # 仅保留 Excel 文件
        excel_files = [
            os.path.join(type_folder, f)
            for f in os.listdir(type_folder)
            if f.lower().endswith((".xls", ".xlsx"))
        ]
        if not excel_files:
            continue
        latest_file = max(excel_files, key=os.path.getmtime)
        print(f"[INFO] 加载最新{get_type_name(data_type)}文件: {os.path.basename(latest_file)}")
        process_file_by_type(latest_file, data_type)


if __name__ == "__main__":
    # 先预加载历史数据，再启动服务
    load_latest_files()
    app.run(host="0.0.0.0", port=8080, debug=False)